﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using DcsGauges.Shared.Packets;
using System.Net;
using NLog;

namespace DcsGauges.Shared.Network
{
    public class UdpNetworkChannel : INetworkChannel
    {
        #region Constants & Redonly

        private readonly Logger                 logger                          = LogManager.GetCurrentClassLogger();

        #endregion


        #region Fields & Properties

        private IPEndPoint                      ipEndpoint                      = null;
        private UdpClient                       sendClient                      = null;
        private UdpClient                       receiveClient                   = null;
        private PacketBuilder                   packetBuilder                   = new PacketBuilder();
        private StringBuffer                    contentBuffer                   = new StringBuffer();

        #endregion


        #region Constructors

        public UdpNetworkChannel(string remoteHost, int sendPort, int receivePort)
        {
            this.sendClient             = new UdpClient("127.0.0.1", sendPort);
            this.receiveClient          = new UdpClient(receivePort);
            this.receiveClient.BeginReceive(ReceiveCallback, null);
        }

        #endregion


        #region Operations
        
        public void Send(Packets.Packet packet)
        {
            byte[] content      = this.packetBuilder.Build(packet);
            this.sendClient.Send(content, content.Length);
        }

        #endregion


        #region Asnyc Callbacks

        private void ReceiveCallback(IAsyncResult result)
        {
            byte[] received = this.receiveClient.EndReceive(result, ref this.ipEndpoint);

            string receivedString = Encoding.UTF8.GetString(received, 0, received.Length);
            logger.Log(LogLevel.Debug, "Received: {0}", receivedString);

            this.contentBuffer.Append(received, received.Length);
            Packet packet       = this.packetBuilder.Build(contentBuffer);
            if(packet != null)
            {
                switch(packet.PacketType)
                {
                    case Packet.PacketTypes.Data:       this.FireDataPacketReceived(packet as DataPacket); break;
                    case Packet.PacketTypes.Handshake:  this.FireHandshakePacketReceived(packet as HandshakePacket); break;
                    case Packet.PacketTypes.Unknown:    logger.Error(string.Format("Received unknown network packet with content: {0}", packet.ToString())); break;
                }
            }

            this.receiveClient.BeginReceive(ReceiveCallback, null);
        }

        #endregion


        #region Events

        public event EventHandler<PacketReceivedEventArgs<Packets.DataPacket>>          DataPacketReceivedEvent;
        public event EventHandler<PacketReceivedEventArgs<Packets.HandshakePacket>>     HandshakePacketReceivedEvent;

        #endregion


        #region Helpers

        private void FireDataPacketReceived(DataPacket packet)
        {
            if (this.DataPacketReceivedEvent != null)
            {
                this.DataPacketReceivedEvent(this, new PacketReceivedEventArgs<DataPacket>(packet));
            }
        }

        private void FireHandshakePacketReceived(HandshakePacket packet)
        {
            if (this.HandshakePacketReceivedEvent != null)
            {
                this.HandshakePacketReceivedEvent(this, new PacketReceivedEventArgs<HandshakePacket>(packet));
            }
        }

        #endregion

    }
}
